# coding=utf-8
import numpy as npy
import operator
'''
KNN算法实现
'''


class kNN:

    """
    k - 对比邻近值个数
    testdata - 用户测试的数据向量[一维数组]
    traindata - 用于训练的向量列表[二维数组]
    labels - 训练数据分类列表[一维数组]
    """

    @classmethod
    def caculate(cls, k, test_data, train_data, labels):

        # 获取训练数据的条目数，第一维的个数
        train_data_size = train_data.shape[0]

        # numpy.tile(A, B) 函数的作用是重复数据，将一个数据复制为多维或者多次重复内容
        # 参数A 表示复制的行数， 参数B 表示每行内容复制的次数
        # 将test_data复制为与train_data相同的行数，以计算每一行与训练数据的区别
        test_data_as_array = npy.tile(test_data, (train_data_size, 1))

        # 计算测试数据集中每一行和相同行上训练数据之间的向量差
        # 可以行到一个多维数据
        dif = test_data_as_array - train_data

        # 将差值矩阵上的每一个值进行平方计算
        sq_dif = dif ** 2

        # 将差值矩阵上的每一行[数组]的值合计为一个数值，结果为一个一维数组
        sum_sq_dif = sq_dif.sum(axis=1)

        # 将差值数组sum_sq_dif上的每一个值进行开平方计算以获得最终的欧氏距离值
        distance = sum_sq_dif ** 0.5

        # argsort 函数返回的值其实不是对数组中的数值进行排序后的数组值，
        # 而是返回的将数组中按数值大小排序后，原来的各对象的索引值（下标值， 第几个），返回的是排序后的索引值
        # 这样数组里存的不是真正的第几个值的数值，而是这些数值原来在数组里的索引，原来是[第几个]
        sort_distance_index = distance.argsort()
        count = {}
        for i in range(0, k):
            # 根据索引值拿到相应的分类值
            vote = labels[sort_distance_index[i]]
            count[vote] = count.get(vote, 0) + 1

        # 将统计好的count里的k个分类，按每个元素的第2个属性（重复次数）进行倒排序
        sort_count = sorted(count.items(), key=operator.itemgetter(1), reverse=True)
        return sort_count[0][0]


